package com.rsbot;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class SilkroadProxy {
    //agent server
    private static final String AGENT_SERVER_HOST = "120.92.23.83";
    private static final int AGENT_SERVER_PORT = 32232;

    //gateway server
    private static final String REAL_LOGIN_SERVER_HOST = "120.92.209.70"; // 例如 login.csro.com
    private static final int REAL_LOGIN_SERVER_PORT = 25882; // cSRO-R 默认登录端口

    // 代理监听端口（客户端连接到 localhost:LOCAL_PORT）
    private static final int LOCAL_PORT = 25882; // 建议与登录服务器端口相同，客户端无需改配置
    // 目标 opcode
    private static final int OPCODE_STARTUP = 0xA103; // 客户端启动响应
//    private static final short OPCODE_LOGIN = (short) 0xB001; // 登录响应
//    private static final short OPCODE_CHAR_DETECT = (short) 0x3013; // 人物检测
    private static final int OPCODE_SERVER_LIST = 0xA101; // 区服列表

    // 连接映射
    private static final Map<SocketChannel, SocketChannel> clientToServerMap = new HashMap<>();
    private static final Map<SocketChannel, SocketChannel> serverToClientMap = new HashMap<>();
    // 游戏服务器信息（从 0xA101 解析）
    private static String gameServerHost = null;
    private static int gameServerPort = 0;

    public static void main(String[] args) throws IOException, InterruptedException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.bind(new InetSocketAddress("127.0.0.1", LOCAL_PORT));
        serverSocket.configureBlocking(false);
        serverSocket.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("Proxy listening on localhost:" + LOCAL_PORT);

        while (true) {
            selector.select();
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iter = selectedKeys.iterator();

            while (iter.hasNext()) {
                SelectionKey key = iter.next();

                if (key.isAcceptable()) {
                    handleAccept(key, selector);
                } else if (key.isReadable()) {
                    handleRead(key);
                }

                iter.remove();
            }
        }
    }

    private static void handleAccept(SelectionKey key, Selector selector) throws IOException, InterruptedException {
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        SocketChannel client = server.accept();
        client.configureBlocking(false);

        // 连接到登录服务器
        SocketChannel serverChannel = SocketChannel.open();
        serverChannel.configureBlocking(false);
        serverChannel.connect(new InetSocketAddress(REAL_LOGIN_SERVER_HOST, REAL_LOGIN_SERVER_PORT));
        while (!serverChannel.finishConnect()) {
            // 等待
            Thread.sleep(100);
        }
        System.out.println("已连接gateway服务器");
        client.register(selector, SelectionKey.OP_READ);
        serverChannel.register(selector, SelectionKey.OP_READ);
        clientToServerMap.put(client, serverChannel);
        serverToClientMap.put(serverChannel, client);
    }

    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel from = (SocketChannel) key.channel();
        SocketChannel to = clientToServerMap.get(from);
        if (to == null) {
            to = serverToClientMap.get(from);
        }
        if (to == null) {
            return;
        }

        ByteBuffer buffer = ByteBuffer.allocate(4096);
        int bytesRead = from.read(buffer);
        if (bytesRead == -1) {
            from.close();
            to.close();
            clientToServerMap.remove(from);
            serverToClientMap.remove(to);
            return;
        }

        buffer.flip();

        // 从服务端读取，检查封包
        if (serverToClientMap.containsKey(from)) {
            processServerPackets(buffer, from);
        }

        // 转发
        to.write(buffer);
    }

    private static void processServerPackets(ByteBuffer buffer, SocketChannel serverChannel) throws IOException {
        // 打印完整的接收到的数据
        System.out.println("收到服务器数据，长度: " + buffer.remaining() + " 字节");

        // 复制缓冲区数据用于打印（不影响原始缓冲区）
        ByteBuffer copyBuffer = buffer.duplicate();
        byte[] rawData = new byte[copyBuffer.remaining()];
        copyBuffer.get(rawData);

        System.out.println("原始十六进制数据:");
        for (int i = 0; i < rawData.length; i++) {
            System.out.printf("%02X ", rawData[i]);
            if ((i + 1) % 16 == 0) {
                System.out.println();
            }
        }
        System.out.println("\n");
        while (buffer.remaining() >= 4) { // 至少要有 size+opcode
            int position = buffer.position(); // 记录当前包开始位置
            int size = buffer.getShort() & 0xFFFF;

            if (buffer.remaining() < size) {
                // 数据不完整，回退，等待下一次 read
                buffer.position(position);
                break;
            }

            int opcode = buffer.getShort();
            System.out.println("opcode:"+opcode);
            if (opcode == OPCODE_STARTUP || opcode == OPCODE_SERVER_LIST) {
                System.out.println("Intercepted packet with opcode: 0x"
                        + Integer.toHexString(opcode & 0xFFFF).toUpperCase());

                // 回到包头，读取完整包
                buffer.position(position);
                byte[] packetData = new byte[size + 2];
                buffer.get(packetData);
                printHexDump(packetData);

                if (opcode == OPCODE_SERVER_LIST) {
                    parseServerList(packetData, serverChannel);
                }
            } else {
                // 跳过整个包
                buffer.position(position + size + 2);
            }
        }
    }

    // 解析区服列表（cSRO-R 需验证实际结构）
    private static void parseServerList(byte[] packetData, SocketChannel serverChannel) throws IOException {
        ByteBuffer buffer = ByteBuffer.wrap(packetData);
        buffer.getShort(); // 跳过 size
        buffer.getShort(); // 跳过 opcode
        byte serverCount = buffer.get();

        if (serverCount > 0) {
            byte nameLength = buffer.get();
            byte[] nameBytes = new byte[nameLength];
            buffer.get(nameBytes);
            gameServerHost = new String(nameBytes, StandardCharsets.UTF_8); // cSRO-R 可能用 GBK
            gameServerPort = buffer.getShort() & 0xFFFF;

            System.out.println("Parsed game server: " + gameServerHost + ":" + gameServerPort);

            // 示例：切换到游戏服务器（实际可能由客户端发起）
            SocketChannel client = serverToClientMap.get(serverChannel);
            if (client != null) {
                SocketChannel newServerChannel = SocketChannel.open();
                newServerChannel.configureBlocking(false);
                newServerChannel.connect(new InetSocketAddress(gameServerHost, gameServerPort));

                while (!newServerChannel.finishConnect()) {
                    // 等待
                }

                clientToServerMap.put(client, newServerChannel);
                serverToClientMap.remove(serverChannel);
                serverToClientMap.put(newServerChannel, client);
                serverChannel.close();
            }
        }
    }

    private static void printHexDump(byte[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.printf("%02X ", data[i]);
            if ((i + 1) % 16 == 0) {
                System.out.println();
            }
        }
        System.out.println();
    }

    // 示例：发送封包
    private static void sendPacketToServer(SocketChannel serverChannel, short opcode, byte[] data) throws IOException {
        short size = (short) (2 + data.length);
        ByteBuffer buffer = ByteBuffer.allocate(size + 2);
        buffer.putShort(size);
        buffer.putShort(opcode);
        buffer.put(data);
        buffer.flip();
        serverChannel.write(buffer);
    }
}